﻿using System;
using System.Collections.Generic;
using Fasterflect;
using Kugar.Core.ExtMethod;
using Kugar.Tool.Web.RequestFieldValidation;
using Kugar.Tool.Web.RequestFieldValidation.Rules;
using Kugar.UI.Web.AdminLTE.Interfaces;
using Microsoft.AspNetCore.Mvc.Rendering;
using Newtonsoft.Json.Linq;

namespace Kugar.UI.Web.AdminLTE.RequestValidate
{
    public class RequestValidateToAdminLte:IClientValidateRender
    {
        public void Render(ViewContext viewContext)
        {
            var collection =
                (RequestFieldCollection) viewContext.HttpContext.RequestServices.GetService(
                    typeof(RequestFieldCollection));

            if (collection.Count<=0)
            {
                return;
            }

            var ruleJson = new JObject();
            var messageJson=new JObject();

            foreach (var field in collection.GetAllFields())
            {
                //JObject fieldJson = null;
                //JObject fieldRuleMessageJson = null;

                var (fieldJson, fieldRuleMessageJson) = getFieldConfig(ruleJson, messageJson, field.FieldName);

                if (field is IRequestArrayField arrayField)
                {
                    var elements = (IEnumerable<IRequestField>)arrayField.GetPropertyValue("ElementFields");
                    foreach (var item in elements)
                    {
                        var (elementJson, elementRuleMessageJson) = getFieldConfig(ruleJson, messageJson, item.FieldName);

                        buildSingleFieldRule(item, elementJson, elementRuleMessageJson);

                    }

                    foreach (var rule in arrayField.Rules)
                    {
                        var errorMsg = rule.BuildErrorMessage(field).ToStringEx();

                        if (rule.GetType()==typeof(ArrayFieldNotEmptyRule<>))
                        {
                            fieldJson["required"] = true;


                            if (!string.IsNullOrWhiteSpace(errorMsg))
                            {
                                fieldRuleMessageJson["required"] = errorMsg;
                            }
                        }

                        
                    }
                }
                else if (field is IRequestField)
                {
                    buildSingleFieldRule(field, fieldJson, fieldRuleMessageJson);
                }
            }

            viewContext.Writer.WriteLine($"var requestRules={(ruleJson.HasData() ? ruleJson.ToString() : "{}")};");

            viewContext.Writer.WriteLine($"var messageJson={(messageJson.HasData()?messageJson.ToString():"{}")};");
        }

        private (JObject fieldRuleJson, JObject fieldMessageJson) getFieldConfig(JObject ruleJson, JObject messageJson, string fieldName)
        {
            JObject fieldJson = null;
            JObject fieldRuleMessageJson = null;

            if (!ruleJson.TryGetValue(fieldName, out var tmp1))
            {
                fieldJson = new JObject();
                ruleJson[fieldName] = fieldJson;
            }
            else
            {
                fieldJson = (JObject)tmp1;
            }

            if (!messageJson.TryGetValue(fieldName, out var tmp2))
            {
                fieldRuleMessageJson = new JObject();
                messageJson[fieldName] = fieldRuleMessageJson;
            }
            else
            {
                fieldRuleMessageJson = (JObject)tmp2;
            }

            return (fieldJson, fieldRuleMessageJson);
        }

        private void buildSingleFieldRule(IRequestField field, JObject fieldJson, JObject fieldRuleMessageJson)
        {
            foreach (var rule in field.Rules)
            {
                var ruleType = rule.GetType();
                var errorMsg = rule.BuildErrorMessage(field).ToStringEx();

                if (ruleType.IsGenericType && ruleType.GetGenericTypeDefinition() == typeof(ComparableValueRule<>))
                {
                    fieldJson["number"] = true;

                    var opt = convertOptToStr((CompareOpt)rule.TryGetValue("Opt"));
                    var value = rule.TryGetValue("CompareValue").ToDecimal();


                    fieldJson[opt] = value;

                    if (!string.IsNullOrWhiteSpace(errorMsg))
                    {
                        fieldRuleMessageJson[opt] = errorMsg;
                    }

                }
                else if (ruleType.IsGenericType && ruleType.GetGenericTypeDefinition() == typeof(EqualToFieldRule<>))
                {
                    var compareToField = rule.TryGetValue("EqualToFieldName").ToStringEx();

                    fieldJson["equalTo"] = $"input[name={compareToField}]:first";

                    if (!string.IsNullOrWhiteSpace(errorMsg))
                    {
                        fieldRuleMessageJson["equalTo"] = errorMsg;
                    }
                }
                else if (ruleType == typeof(StringLengthRule))
                {
                    var tmpRule = (StringLengthRule)rule;

                    if (tmpRule.IsAscIILength)
                    {
                        fieldJson["asciilength"] = new JArray()
                        {
                            tmpRule.MinLength >= 0? (int?)tmpRule.MinLength:null,
                            tmpRule.MaxLength >= 0? (int?)tmpRule.MaxLength:null,
                        };

                        if (!string.IsNullOrWhiteSpace(errorMsg))
                        {
                            fieldRuleMessageJson["asciilength"] = errorMsg;
                        }
                    }
                    else
                    {
                        if (tmpRule.MaxLength >= 0)
                        {
                            fieldJson["maxlength"] = tmpRule.MaxLength;
                        }

                        if (tmpRule.MinLength >= 0)
                        {
                            fieldJson["minlength"] = tmpRule.MinLength;
                        }

                        if (!string.IsNullOrWhiteSpace(errorMsg))
                        {
                            fieldRuleMessageJson["maxlength"] = errorMsg;
                            fieldRuleMessageJson["minlength"] = errorMsg;
                        }
                    }
                    
                }
                else if (ruleType == typeof(NotEmptyCheckRule) || ruleType == typeof(ExistsCheckRule))
                {
                    fieldJson["required"] = true;

                    if (!string.IsNullOrWhiteSpace(errorMsg))
                    {
                        fieldRuleMessageJson["required"] = errorMsg;
                    }
                }
                else if ( rule is ClientRemoteCheckRule remoteRule)
                {
                     fieldJson["customRemote"] =new JObject()
                                               {
                                                   ["url"] =remoteRule.Url,
                                                   ["method"] =remoteRule.Method,
                                                   ["withNames"] =remoteRule.WithFieldNames.ToJArray()
                                               };

                    if (!string.IsNullOrWhiteSpace(errorMsg))
                    {
                        fieldRuleMessageJson["customRemote"] = errorMsg;
                    }
                }
                else if (ruleType.IsGenericType && ruleType.GetGenericTypeDefinition() == typeof(ValueInRule<>))
                {
                    fieldJson["inarray"]=new JArray(rule.FastGetValue("Values"));
                }
                else if (rule is EMailRule)
                {
                    fieldJson["email"]=new JObject()
                    {
                    };
                }
                else if (rule is MobileRule)
                {
                    fieldJson["mobile"]=new JObject();
                }
            }
        }

        private string convertOptToStr(CompareOpt opt)
        {
            switch (opt)
            {
                case CompareOpt.GT:
                    return "min";
                case CompareOpt.GTE:
                    return "gte";
                case CompareOpt.LT:
                    return "max";
                case CompareOpt.LTE:
                    return "lte";
                case CompareOpt.EQ:
                    return "eq";
                default:
                    throw new ArgumentOutOfRangeException(nameof(opt), opt, null);
            }
        }

        private static readonly string[] _jsFiles = new[]
                                                    {
                                                        "/requestvalidate/Content/js/jquery.format.js",
                                                        "/requestvalidate/Content/js/jquery.validate.min.js",
                                                        "/requestvalidate/Content/js/messages_zh.min.js",
                                                        "/requestvalidate/Content/js/validator.js"
                                                    };
        public string[] Scripts()
        {
            return _jsFiles;
        }

        public string[] CssFilesUrl()
        {
            return null;
        }
    }
    
}
